package limit.aspect;

import limit.DistributeLock;
import limit.execption.DistributeLockException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class DistributeLockAspect {

    private RedissonClient redissonClient;

    public DistributeLockAspect(RedissonClient redissonClient){
        this.redissonClient = redissonClient;
    }

    @Around("@annotation(limit.DistributeLock)")
    public Object process(ProceedingJoinPoint pjp) throws Exception {
        Object response = null;
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
        String key = distributeLock.key();
        if ("NONE".equals(key)){
            if ("NONE".equals(distributeLock.keyExpression())) {
                throw new DistributeLockException("no lock key found...");
            }
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(distributeLock.keyExpression());
            EvaluationContext context = new StandardEvaluationContext();
            Object[] args = pjp.getArgs();//获取参数
            StandardReflectionParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer();//获取运行时参数名称
            String[] parameterNames = discoverer.getParameterNames(method);
            //将运行时参数绑定到context中
            if (parameterNames != null){
                for (int i = 0; i < parameterNames.length; i++) {
                    context.setVariable(parameterNames[i],args[i]);
                }
            }
            //解析表达式，获取结果
            key = String.valueOf(expression.getValue(context));
        }

        String scene = distributeLock.scene();
        String lockKey = scene + "#" + key;
        int expireTime = distributeLock.expireTime();
        int waitTime = distributeLock.waitTime();
        RLock rLock = redissonClient.getLock(lockKey);
        boolean lockResult = false;
        if (waitTime == -1){
            if (expireTime == -1){
                rLock.lock();
            } else {
                rLock.lock(expireTime, TimeUnit.MILLISECONDS);
            }
            lockResult = true;
        } else {
            if (expireTime == -1){
                lockResult = rLock.tryLock(waitTime,TimeUnit.MILLISECONDS);
            } else {
                lockResult = rLock.tryLock(waitTime,expireTime,TimeUnit.MILLISECONDS);
            }
        }
        if (!lockResult){
            throw new DistributeLockException("acquire lock failed... key:" + lockKey);
        }
        try {
            response = pjp.proceed();
        } catch (Throwable e){
            throw new Exception(e);
        } finally {
            rLock.unlock();
        }
        return response;
    }
}
